#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "standardchanmanager.h"
#include "components/board.h"
#include "components/postid.h"
#include "mimetypes.h"
#include "mockobjects/mockchanagent.h"
#include "mockobjects/mockthreadstorage.h"
#include "mockobjects/mockcookiestorage.h"
#include "mockobjects/mockagentfactory.h"
#include "chanagents/defaultagent.h"

namespace
{
	using namespace Wishmaster;
	using ::testing::_;
	using ::testing::Return;
	using ::testing::AnyNumber;
	using ::testing::NiceMock;

	static const QString hostname = "www.test.test.test.com";

	class TestChanManager : public ::testing::Test
	{
	public:
		TestChanManager() : ::testing::Test(), agent(hostname), defaultThreadId(hostname, "/b/", 12345678) {}

	protected:
		void SetUp()
		{
			new MimeTypes();
			manager = new Wishmaster::StandardChanManager(NULL, new NiceMock<MockObjects::MockThreadStorage>(), new NiceMock<MockObjects::MockCookieStorage>());
			manager->initialize();
		}

		void TearDown()
		{
			manager->shutdown();
			delete manager;
			delete MimeTypes::instance();
		}

		void createChan(ChanAgent* agent)
		{
			m_chan = new Chan(hostname, agent, manager);
			m_board = new Board("/b/", m_chan);
			m_chan->addBoard(m_board);
			manager->chanlist()->addChan(m_chan);
		}

		void addThreadToBoard(const ThreadId& threadId)
		{
			ThreadPtr thread = ThreadPtr(new Thread(threadId, m_board));
			m_board->visibleThreads()->push_back(thread);
		}

		void addPostsWithAttachmentsToThread(const ThreadId& threadId)
		{
			QUrl thumblink(QString("http://%1/thumb.jpg").arg(hostname));
			QUrl link(QString("http://%1/pic.jpg").arg(hostname));
			ThreadPtr thread = m_board->visibleThreads()->getNth(0);
			PostPtr post1 = PostPtr(new Post(PostId(threadId, PostNumId)));
			AttachmentPtr attach = Attachment::create(thumblink, link);
			post1->addAttachment(attach);
			thread->appendPost(post1);
		}

		ChanAgent* createChanAgent(const QString& host)
		{
			return new DefaultAgent(host);
		}

		QByteArray chanlistTestData()
		{
			QByteArray arr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
			arr.append("<chans>");
			arr.append("<chan name=\"chan1\" agent=\"test_agent\" url=\"chan1\">");
			arr.append("</chan>");
			arr.append("</chans>");
			return arr;
		}

		Wishmaster::ChanManager* manager;
		Wishmaster::Chan* m_chan;
		Wishmaster::Board* m_board;

		NiceMock<MockObjects::MockChanAgent> agent;
		ThreadId defaultThreadId;

		static const int PostNumId = 12345679;
	};

	TEST_F(TestChanManager, RequestBoardIndex)
	{
		EXPECT_CALL(agent,requestBoardIndex(QString("/b/"), 0)).Times(1);

		createChan(&agent);
		ASSERT_TRUE(manager->requestBoardIndex(hostname, "/b/", 0));
	}

	TEST_F(TestChanManager, RequestThread)
	{
		PostId postId;

		EXPECT_CALL(agent,requestThread(defaultThreadId, postId)).Times(1);

		createChan(&agent);
		addThreadToBoard(defaultThreadId);

		ASSERT_TRUE(manager->requestThread(defaultThreadId));
	}

	TEST_F(TestChanManager, RequestThumbnail)
	{
		PostId postId(defaultThreadId, 12345679);
		QUrl url(QString("http://%1/thumbnail.jpg").arg(hostname));

		EXPECT_CALL(agent, requestThumbnail(postId, 0, url)).Times(1);

		createChan(&agent);
		addThreadToBoard(defaultThreadId);

		ASSERT_TRUE(manager->requestThumbnail(postId, 0, url));
	}

	TEST_F(TestChanManager, RequestPic)
	{
		QUrl url(QString("http://%1/pic.jpg").arg(hostname));
		PostId postId(defaultThreadId, PostNumId);

		EXPECT_CALL(agent, requestPic(postId, 0, url)).Times(1);

		createChan(&agent);
		addThreadToBoard(defaultThreadId);
		addPostsWithAttachmentsToThread(defaultThreadId);

		ASSERT_TRUE(manager->requestPic(postId, 0));
	}

	TEST_F(TestChanManager, DownloadFile)
	{
		QUrl url(QString("http://%1/pic.jpg").arg(hostname));
		PostId postId(defaultThreadId, PostNumId);
		QString filename("/tmp/filename.tmp");

		EXPECT_CALL(agent, downloadFile(postId, 0, url, filename)).Times(1);

		createChan(&agent);
		addThreadToBoard(defaultThreadId);
		addPostsWithAttachmentsToThread(defaultThreadId);

		ASSERT_TRUE(manager->downloadFile(postId, 0, filename));
	}

	TEST_F(TestChanManager, RequestCaptcha)
	{
		EXPECT_CALL(agent, getCaptcha(defaultThreadId));

		createChan(&agent);
		addThreadToBoard(defaultThreadId);

		ASSERT_TRUE(manager->requestCaptcha(defaultThreadId));
	}

	TEST_F(TestChanManager, CollectGarbage)
	{
		MockObjects::MockThreadStorage* storage = new MockObjects::MockThreadStorage();

		manager->setThreadStorage(storage);
		EXPECT_CALL(*storage, setMaxThreads(_)).Times(AnyNumber());
		EXPECT_CALL(*storage, deleteOldThreads(hostname, QString("/b/"))).Times(1).
				WillRepeatedly(Return(QList<unsigned long>()));
		createChan(&agent);
		m_chan->setThreadStorage(storage);
		manager->collectGarbage();
	}

	TEST_F(TestChanManager, loadChanList_usesAgentFactory)
	{
		ChanAgent* chan1 = createChanAgent("chan1");
		MockObjects::MockAgentFactory factory;
		EXPECT_CALL(factory, makeAgent(_, _)).WillOnce(Return(chan1));
		QByteArray data = chanlistTestData();

		ASSERT_TRUE(manager->loadChanList(data, &factory));
	}

	TEST_F(TestChanManager, loadChanList_fillsChanlist)
	{
		ChanAgent* chan1 = createChanAgent("chan1");
		MockObjects::MockAgentFactory factory;
		EXPECT_CALL(factory, makeAgent(_, _)).WillOnce(Return(chan1));
		QByteArray data = chanlistTestData();

		manager->loadChanList(data, &factory);

		ASSERT_EQ(1, manager->chanlist()->chansCount());
	}

}
